package com.heima.article.job;

import com.alibaba.fastjson.JSON;
import com.heima.article.service.HotArticleService;
import com.heima.common.exception.CustException;
import com.heima.model.common.constants.HotArticleConstants;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.mess.app.AggBehaviorDTO;
import com.heima.model.mess.app.NewBehaviorDTO;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.*;

import java.util.stream.Collectors;

@Component
@Slf4j
public class UpdateHotArticleJob {
    @Autowired
    StringRedisTemplate redisTemplate;
    @Autowired
    HotArticleService hotArticleService;

    @XxlJob("updateHotArticleJob")
    public ReturnT updateHotArticleHandler(String params) {
        log.info("热文章分值更新 调度任务开始执行....");
        // 1. 获取redis 行为列表中待处理数据
        List<NewBehaviorDTO> behaviorList = getRedisBehaviorList();
        if (CollectionUtils.isEmpty(behaviorList)) {
            log.info("热文章分值更新: 太冷清了 未产生任何文章行为 调度任务完成....");
            return ReturnT.SUCCESS;
        }
        // 2. 将数据按照文章分组  进行聚合统计 得到待更新的数据列表
        List<AggBehaviorDTO> aggBehaviorList = getAggBehaviorList(behaviorList);
        if (CollectionUtils.isEmpty(aggBehaviorList)) {
            log.info("热文章分值更新: 太冷清了 未产生任何文章行为 调度任务完成....");
            return ReturnT.SUCCESS;
        }
        // 3. TODO 更新数据库文章分值
        aggBehaviorList.forEach(hotArticleService::updateApArticle);
        log.info("热文章分值更新: 调度任务完成....");
        return ReturnT.SUCCESS;
    }

    /**
     * 按文章分组  每个文章的所有行为 进行聚合处理
     * @param behaviorList 最近10s产生的文章行为
     * @return
     */
    private List<AggBehaviorDTO> getAggBehaviorList(List<NewBehaviorDTO> behaviorList) 	   {
        List<AggBehaviorDTO> aggBehaviorList = new ArrayList<>();

        //1 按照文章id分组，获取对应分组下的文章列表
        Map<Long, List<NewBehaviorDTO>> map =
                behaviorList.stream()
                        .collect(Collectors.groupingBy(NewBehaviorDTO::getArticleId));

        //2 计算每个分组的结果
        // 遍历map     文章id     对应行为
        map.forEach((articleId, messList) -> {
            Optional<AggBehaviorDTO> reduceResult =
                    messList.stream()
                            .map(behavior -> {
                                // 将每个行为数据  都封装为聚合行为类型
                                AggBehaviorDTO aggBehavior = new AggBehaviorDTO();
                                aggBehavior.setArticleId(articleId);
                                switch (behavior.getType()) {
                                    case LIKES:
                                        // 设置 点赞数量
                                        aggBehavior.setLike(behavior.getAdd());
                                        break;
                                    case VIEWS:
                                        // 设置 阅读数量
                                        aggBehavior.setView(behavior.getAdd());
                                        break;
                                    case COMMENT:
                                        // 设置 评论数量
                                        aggBehavior.setComment(behavior.getAdd());
                                        break;
                                    case COLLECTION:
                                        // 设置 收藏数量
                                        aggBehavior.setCollect(behavior.getAdd());
                                        break;
                                    default:
                                }
                                return aggBehavior;
                            }).reduce((a1, a2) -> {
                        a1.setLike(a1.getLike() + a2.getLike());
                        a1.setView(a1.getView() + a2.getView());
                        a1.setComment(a1.getComment() + a2.getComment());
                        a1.setCollect(a1.getCollect() + a2.getCollect());
                        return a1;
                    });
            if (reduceResult.isPresent()) {
                // 聚合结果
                AggBehaviorDTO aggBehavior = reduceResult.get();
                log.info("热点文章 聚合计算结果  ===>{}", aggBehavior);
                aggBehaviorList.add(aggBehavior);
            }
        });
        return aggBehaviorList;
    }

    /**
     * 获取redis list列表中的待处理行为数据
     * @return
     */
    private List<NewBehaviorDTO> getRedisBehaviorList() {
        try {
            //调用lua脚本并执行
            DefaultRedisScript<List> redisScript = new DefaultRedisScript<>();
            // 设置脚本的返回结果
            redisScript.setResultType(List.class);

            //lua文件存放在resources目录下的redis文件夹内
            redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("redis.lua")));
            // 执行lua脚本
            List<String> result = redisTemplate.execute(redisScript, Arrays.asList(HotArticleConstants.HOT_ARTICLE_SCORE_BEHAVIOR_LIST));

            // 将查询到的result 解析为NewsBehaviorDTO行为对象  并存储到集合返回
            return result.stream()
                    .map(jsonStr-> JSON.parseObject(jsonStr,NewBehaviorDTO.class))
                    .collect(Collectors.toList());
        } catch (Exception e) {
            e.printStackTrace();
            CustException.cust(AppHttpCodeEnum.SERVER_ERROR,"执行lua脚本失败");
        }
        return null;
    }
}